ปลดล็อกศักยภาพของ useRef hook ใน React เพื่อการจัดการ state ที่เปลี่ยนแปลงได้และ DOM ที่มีประสิทธิภาพ ซึ่งจำเป็นสำหรับการสร้างแอปพลิเคชันที่แข็งแกร่งและขยายได้ทั่วโลก
React useRef: การเรียนรู้เชิงลึกเกี่ยวกับการจัดเก็บค่าที่เปลี่ยนแปลงได้และการจัดการการอ้างอิง DOM สำหรับนักพัฒนาระดับโลก
ในโลกของการพัฒนาเว็บที่เปลี่ยนแปลงตลอดเวลา การสร้างส่วนต่อประสานผู้ใช้ (UI) ที่มีประสิทธิภาพและโต้ตอบได้เป็นสิ่งสำคัญยิ่ง สำหรับวิศวกร frontend ที่ทำงานในระดับสากล การทำความเข้าใจในความแตกต่างของการจัดการ state และการจัดการ DOM เป็นกุญแจสำคัญในการมอบประสบการณ์ผู้ใช้ที่ยอดเยี่ยม React ซึ่งมีสถาปัตยกรรมแบบคอมโพเนนต์ นำเสนอเครื่องมือที่ทรงพลังเพื่อให้บรรลุเป้าหมายนี้ และในบรรดาเครื่องมือเหล่านี้ useRef hook โดดเด่นในฐานะยูทิลิตี้อเนกประสงค์สำหรับจัดการค่าที่เปลี่ยนแปลงได้ซึ่งคงอยู่ตลอดการ re-render โดยไม่กระตุ้นให้เกิดการ re-render และสำหรับการอ้างอิงถึงองค์ประกอบ DOM โดยตรง
คู่มือฉบับสมบูรณ์นี้มีจุดมุ่งหมายเพื่อไขความกระจ่างเกี่ยวกับ useRef โดยให้มุมมองระดับโลกเกี่ยวกับการใช้งาน ประโยชน์ และแนวทางปฏิบัติที่ดีที่สุด เราจะสำรวจว่า useRef สามารถปรับปรุงเวิร์กโฟลว์การพัฒนาของคุณ เพิ่มประสิทธิภาพของแอปพลิเคชัน และรับประกันว่าแอปพลิเคชัน React ของคุณจะแข็งแกร่งและสามารถขยายขนาดได้ โดยไม่คำนึงถึงตำแหน่งทางภูมิศาสตร์หรือความท้าทายทางเทคนิคเฉพาะที่โครงการของคุณเผชิญ
การทำความเข้าใจแนวคิดหลักของ useRef
โดยหัวใจหลักแล้ว useRef คือ hook ที่ส่งคืนออบเจ็กต์ ref ที่เปลี่ยนแปลงค่าได้ ออบเจ็กต์นี้มีคุณสมบัติเดียวคือ .current ซึ่งสามารถกำหนดค่าเริ่มต้นด้วยอาร์กิวเมนต์ที่ส่งมา (initialValue) สิ่งสำคัญของออบเจ็กต์ ref คือคุณสมบัติ .current สามารถเปลี่ยนแปลงได้และยังคงอยู่ระหว่างการ re-render ซึ่งหมายความว่าการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับ ref.current จะไม่ทำให้คอมโพเนนต์เกิดการ re-render
พฤติกรรมนี้ทำให้ useRef แตกต่างจาก state ของคอมโพเนนต์ที่จัดการโดย useState เมื่อ state เปลี่ยนแปลง React จะกำหนดเวลาให้ re-render เพื่อสะท้อน UI ที่อัปเดต อย่างไรก็ตาม เมื่อคุณเปลี่ยนแปลงค่าของคุณสมบัติ .current ของ ref คอมโพเนนต์จะไม่ re-render สิ่งนี้ทำให้ useRef เหมาะสำหรับสถานการณ์ที่คุณต้องการเก็บค่าที่สามารถเปลี่ยนแปลงได้แต่ไม่จำเป็นต้องแสดงผลบน UI ในทันที หรือสำหรับการโต้ตอบกับองค์ประกอบ DOM โดยตรง
ควรใช้ useRef เมื่อใด: กรณีการใช้งานที่สำคัญ
ความสามารถรอบด้านของ useRef ทำให้สามารถนำไปใช้ในสถานการณ์การพัฒนาทั่วไปได้หลายอย่าง เรามาสำรวจสิ่งเหล่านี้โดยมุ่งเน้นว่ามีประโยชน์ต่อทีมพัฒนาระดับโลกอย่างไร:
1. การเก็บค่าที่เปลี่ยนแปลงได้ซึ่งไม่ทำให้เกิดการ Re-render
ลองนึกภาพว่าคุณกำลังสร้างฟีเจอร์ที่ติดตามจำนวนครั้งที่ผู้ใช้คลิกปุ่ม แต่จำนวนนี้ไม่จำเป็นต้องแสดงบนหน้าจอแบบเรียลไทม์ การใช้ useState สำหรับสิ่งนี้จะกระตุ้นให้เกิดการ re-render ที่ไม่จำเป็น ซึ่งอาจส่งผลกระทบต่อประสิทธิภาพ โดยเฉพาะอย่างยิ่งบนอุปกรณ์ที่มีสเปคต่ำซึ่งพบได้ทั่วไปในบางประเทศกำลังพัฒนาหรือในช่วงที่มีการใช้งานเครือข่ายหนาแน่น
useRef เป็นทางออกที่ยอดเยี่ยม:
import React, { useRef } from 'react';
function ClickCounter() {
const clickCount = useRef(0);
const handleClick = () => {
clickCount.current = clickCount.current + 1;
console.log('Button clicked:', clickCount.current);
// No re-render occurs here.
};
return (
);
}
export default ClickCounter;
ในตัวอย่างนี้ clickCount.current จะเพิ่มขึ้นทุกครั้งที่คลิก ตัวคอมโพเนนต์เองยังคงนิ่ง แต่ค่าภายใน ref จะได้รับการอัปเดต สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับตัวจับเวลา, interval หรือกระบวนการเบื้องหลังใดๆ ที่คุณต้องการรักษาสถานะที่เปลี่ยนแปลงได้โดยไม่ส่งผลกระทบต่อผลลัพธ์ที่แสดงผล
2. การเข้าถึงและจัดการองค์ประกอบ DOM
หนึ่งในการใช้งาน useRef ที่พบบ่อยที่สุดคือการเข้าถึงโหนด DOM โดยตรง ซึ่งจำเป็นสำหรับงานต่างๆ เช่น การจัดการโฟกัส, การสั่งงานอนิเมชั่นแบบ imperative หรือการทำงานร่วมกับไลบรารีของบุคคลที่สามที่ต้องพึ่งพา DOM โดยปกติแล้ว ธรรมชาติการทำงานแบบ declarative ของ React หมายความว่าคุณไม่จำเป็นต้องแตะต้อง DOM โดยตรง แต่ก็มีข้อยกเว้น
ลองพิจารณาแอปพลิเคชันที่คุณต้องการโฟกัสช่องป้อนข้อมูลโดยอัตโนมัติเมื่อคอมโพเนนต์ถูก mount นี่คือวิธีที่ useRef ช่วยอำนวยความสะดวกในเรื่องนี้:
import React, { useRef, useEffect } from 'react';
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// The ref.current will be populated after the initial render
if (inputRef.current) {
inputRef.current.focus();
}
}, []); // Empty dependency array ensures this runs only once after the initial render
return (
);
}
export default AutoFocusInput;
ในโค้ดส่วนนี้ attribute ref ถูกแนบไปกับองค์ประกอบ <input> ในระหว่างการ render ครั้งแรก React จะกำหนดโหนด DOM ที่แท้จริงของ input ให้กับ inputRef.current จากนั้น useEffect hook จะเรียกใช้เมธอด .focus() แบบดั้งเดิมบนโหนด DOM นี้ เพื่อให้แน่ใจว่าช่องป้อนข้อมูลจะถูกโฟกัสเมื่อคอมโพเนนต์ mount รูปแบบนี้มีค่าอย่างยิ่งสำหรับการสร้างฟอร์มที่ใช้งานง่ายและปรับปรุงการเข้าถึง (accessibility) ในสภาพแวดล้อมเบราว์เซอร์และระบบปฏิบัติการต่างๆ ทั่วโลก
3. การเก็บค่าก่อนหน้าของ State หรือ Props
บางครั้ง คุณจำเป็นต้องเปรียบเทียบค่าปัจจุบันของ state หรือ prop กับค่าก่อนหน้า ตัวอย่างเช่น คุณอาจต้องการบันทึกการเปลี่ยนแปลงหรือดำเนินการบางอย่างเมื่อ prop ที่ระบุมีการเปลี่ยนแปลงจากการ render ครั้งล่าสุดเท่านั้น
useRef สามารถเก็บค่าก่อนหน้าได้อย่างมีประสิทธิภาพ:
import React, { useState, useRef, useEffect } from 'react';
function PreviousValueDisplay({ value }) {
const [currentValue, setCurrentValue] = useState(value);
const prevValueRef = useRef();
useEffect(() => {
// Store the current value before the next render
prevValueRef.current = currentValue;
}, [currentValue]); // This effect runs after every update to currentValue
const handleIncrement = () => {
setCurrentValue(prev => prev + 1);
};
return (
Current Value: {currentValue}
Previous Value: {prevValueRef.current}
);
}
export default PreviousValueDisplay;
ในที่นี้ prevValueRef.current จะเก็บค่าของ currentValue จากรอบการ render *ก่อนหน้า* ซึ่งทำได้โดยการอัปเดตค่า current ของ ref ในตอนท้ายของ effect หลังจากที่ state ใหม่ถูกกำหนดแล้ว แต่ก่อนที่คอมโพเนนต์จะ re-render เสร็จสมบูรณ์สำหรับรอบถัดไป เทคนิคนี้มีความสำคัญอย่างยิ่งสำหรับการนำฟีเจอร์ต่างๆ ไปใช้งาน เช่น การตรวจจับการเปลี่ยนแปลง หรือการวิเคราะห์ประสิทธิภาพที่ต้องอาศัยข้อมูลในอดีต
4. การจัดการ Timers และ Intervals
เมื่อทำงานกับ setTimeout หรือ setInterval บ่อยครั้งที่จำเป็นต้องเก็บ ID ของตัวจับเวลาเพื่อล้าง interval ในภายหลัง useRef เหมาะสำหรับสิ่งนี้อย่างยิ่ง เพราะมันช่วยให้คุณสามารถเก็บ ID ของตัวจับเวลาไว้ได้ตลอดการ re-render โดยไม่ทำให้เกิดการ re-render ที่ไม่จำเป็น
import React, { useState, useRef, useEffect } from 'react';
function TimerComponent() {
const [seconds, setSeconds] = useState(0);
const intervalIdRef = useRef(null);
useEffect(() => {
// Start the interval when the component mounts
intervalIdRef.current = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);
// Cleanup function to clear the interval when the component unmounts
return () => {
if (intervalIdRef.current) {
clearInterval(intervalIdRef.current);
}
};
}, []); // Empty dependency array means this effect runs once on mount and cleans up on unmount
const handleStopTimer = () => {
if (intervalIdRef.current) {
clearInterval(intervalIdRef.current);
console.log('Timer stopped.');
}
};
return (
Timer: {seconds}s
);
}
export default TimerComponent;
ในตัวอย่างนี้ ID ที่ส่งคืนโดย setInterval จะถูกเก็บไว้ใน intervalIdRef.current จากนั้น ID นี้จะถูกใช้ในฟังก์ชัน cleanup ของ useEffect เพื่อล้าง interval เมื่อคอมโพเนนต์ unmount ซึ่งจะช่วยป้องกันหน่วยความจำรั่วไหล (memory leaks) รูปแบบนี้สามารถนำไปใช้ได้กับทุกสถานการณ์สำหรับการจัดการการทำงานแบบ asynchronous ในแอปพลิเคชัน React ใดๆ เพื่อให้แน่ใจว่าการทำงานจะเชื่อถือได้ในสภาพแวดล้อมการทำงานที่หลากหลาย
`useRef` กับ `useState`: ความแตกต่างที่สำคัญ
การทำความเข้าใจว่าเมื่อใดควรใช้ useRef และเมื่อใดควรเลือกใช้ useState เป็นสิ่งสำคัญอย่างยิ่ง ความแตกต่างหลักอยู่ที่ผลกระทบต่อการ re-render:
useState: การอัปเดตจะกระตุ้นให้เกิดการ re-render ของคอมโพเนนต์ เหมาะสำหรับข้อมูลที่ส่งผลโดยตรงต่อ UI และจำเป็นต้องแสดงผลให้ผู้ใช้เห็นในทันที ตัวอย่างเช่น การป้อนข้อมูลของผู้ใช้ในฟอร์ม, การสลับการแสดงผลของ modal หรือการแสดงข้อมูลที่ดึงมาuseRef: การอัปเดตค่า.currentจะ ไม่ กระตุ้นให้เกิดการ re-render ทำให้เหมาะสำหรับการเก็บข้อมูลที่เปลี่ยนแปลงได้ซึ่งไม่จำเป็นต้องทำให้ UI อัปเดต หรือสำหรับการโต้ตอบกับ DOM โดยตรง
มุมมองระดับโลกในการเลือกใช้: เมื่อพัฒนาสำหรับผู้ใช้ทั่วโลก การเพิ่มประสิทธิภาพเป็นสิ่งสำคัญอย่างยิ่ง การใช้ useState สำหรับค่าที่ไม่ส่งผลกระทบต่อ UI ในทันทีอาจนำไปสู่การ re-render ที่ไม่จำเป็น ทำให้แอปพลิเคชันช้าลง โดยเฉพาะสำหรับผู้ใช้ที่มีอุปกรณ์ประสิทธิภาพต่ำหรือการเชื่อมต่ออินเทอร์เน็ตที่ช้า ในกรณีเช่นนี้ useRef จะกลายเป็นเครื่องมือที่ทรงคุณค่าในการรักษาประสบการณ์ผู้ใช้ที่ราบรื่นและตอบสนองได้ดี
เทคนิคและข้อควรพิจารณาขั้นสูงสำหรับ `useRef`
นอกเหนือจากการใช้งานพื้นฐานแล้ว useRef ยังสามารถนำไปใช้ในรูปแบบที่ซับซ้อนมากขึ้นได้:
1. การจัดการการอ้างอิง DOM หลายรายการ
คุณสามารถสร้าง ref หลายรายการเพื่อจัดการองค์ประกอบ DOM ที่แตกต่างกันภายในคอมโพเนนต์เดียว ซึ่งเป็นเรื่องปกติในเลย์เอาต์ที่ซับซ้อนหรือคอมโพเนนต์ที่ต้องจัดการโฟกัสข้ามองค์ประกอบที่โต้ตอบได้หลายรายการ
import React, { useRef } from 'react';
function FocusManager() {
const input1Ref = useRef(null);
const input2Ref = useRef(null);
const focusFirstInput = () => {
input1Ref.current.focus();
};
const focusSecondInput = () => {
input2Ref.current.focus();
};
return (
);
}
export default FocusManager;
สิ่งนี้ช่วยให้สามารถควบคุมองค์ประกอบที่โต้ตอบได้อย่างละเอียด เพิ่มความสามารถในการใช้งานและการเข้าถึงได้ โดยเฉพาะสำหรับผู้ใช้ที่ต้องพึ่งพาการนำทางด้วยคีย์บอร์ด
2. Custom Hooks กับ `useRef`
useRef เป็นส่วนประกอบที่ทรงพลังสำหรับการสร้าง custom hooks ที่ห่อหุ้มตรรกะที่นำกลับมาใช้ใหม่ได้ ตัวอย่างเช่น custom hook เพื่อติดตามสถานะก่อนหน้าของ prop หรือเพื่อจัดการโฟกัสข้ามคอมโพเนนต์
นี่คือตัวอย่างง่ายๆ ของ custom hook เพื่อติดตามค่าก่อนหน้า:
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}); // Store current value before next render pass
return ref.current;
}
// Usage in a component:
// const prevCount = usePrevious(count);
รูปแบบนี้ส่งเสริมการนำโค้ดกลับมาใช้ใหม่และความสามารถในการบำรุงรักษา ซึ่งมีความสำคัญอย่างยิ่งสำหรับทีมพัฒนาขนาดใหญ่ที่ทำงานร่วมกันในโครงการระดับโลก
3. ข้อควรพิจารณาสำหรับ Server-Side Rendering (SSR)
เมื่อใช้งาน SSR กับเฟรมเวิร์กอย่าง Next.js การจัดการ DOM โดยตรงผ่าน useRef จำเป็นต้องทำอย่างระมัดระวัง โหนด DOM จะพร้อมใช้งานบนฝั่ง client เท่านั้นหลังจากการ render ครั้งแรก ดังนั้น โค้ดใดๆ ที่เข้าถึง ref.current สำหรับการดำเนินการกับ DOM ควรอยู่ใน useEffect hook เนื่องจาก hook เหล่านี้จะทำงานในเบราว์เซอร์เท่านั้น
ตัวอย่าง:
import React, { useRef, useEffect } from 'react';
function ClientSideOnlyComponent() {
const myDivRef = useRef(null);
useEffect(() => {
// This code only runs in the browser
if (myDivRef.current) {
console.log('DOM element found:', myDivRef.current);
myDivRef.current.style.backgroundColor = 'lightblue';
}
}, []); // Runs only once after initial client-side render
return (
This content is rendered on the client.
);
}
export default ClientSideOnlyComponent;
สิ่งนี้ทำให้แน่ใจได้ว่าแอปพลิเคชันของคุณยังคงมีประสิทธิภาพในระหว่างการ render ครั้งแรกบนเซิร์ฟเวอร์ และทำการ hydrate บน client ได้อย่างถูกต้องโดยไม่มีข้อผิดพลาด
4. ผลกระทบด้านประสิทธิภาพ: เมื่อใดที่ควรหลีกเลี่ยงการ Re-render
useRef เป็นเครื่องมือที่ทรงพลังสำหรับการเพิ่มประสิทธิภาพ ด้วยการเก็บข้อมูลที่เปลี่ยนแปลงได้ซึ่งไม่ต้องการการอัปเดต UI ในทันที คุณจะป้องกันการ re-render ที่ไม่จำเป็นได้ ซึ่งส่งผลกระทบอย่างยิ่งในแอปพลิเคชันที่ซับซ้อนซึ่งมีคอมโพเนนต์จำนวนมากหรือมีการเปลี่ยนแปลง state บ่อยครั้ง
บริบทด้านประสิทธิภาพระดับโลก: ในภูมิภาคที่มีความเร็วอินเทอร์เน็ตไม่คงที่หรือผู้ใช้ที่ใช้อุปกรณ์รุ่นเก่า การลดการ re-render ให้น้อยที่สุดสามารถปรับปรุงประสิทธิภาพที่ผู้ใช้รับรู้และความพึงพอใจได้อย่างมาก การใช้ useRef สำหรับ state ที่ไม่เกี่ยวข้องกับการแสดงผลอาจเป็นการตัดสินใจเชิงกลยุทธ์เพื่อให้แน่ใจว่าแอปพลิเคชันของคุณยังคงเข้าถึงได้และตอบสนองได้ดีทั่วโลก
แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้ `useRef` ในระดับสากล
เพื่อเพิ่มประสิทธิภาพสูงสุดของ useRef ในบริบทการพัฒนาระดับโลก ควรปฏิบัติตามแนวทางที่ดีที่สุดเหล่านี้:
- หลักการตั้งชื่อที่ชัดเจน: ใช้ชื่อที่สื่อความหมายสำหรับ ref ของคุณ (เช่น
inputRef,timerIdRef,prevCountRef) เพื่อปรับปรุงความสามารถในการอ่านโค้ดสำหรับสมาชิกในทีมนานาชาติที่อาจมีภาษาแม่ต่างกัน - ค่าเริ่มต้น: กำหนดค่าเริ่มต้นที่เหมาะสมสำหรับ ref ของคุณเสมอ (เช่น
nullสำหรับ DOM ref,0สำหรับตัวนับ) เพื่อป้องกันข้อผิดพลาดระหว่างการ render ครั้งแรก useEffectสำหรับการจัดการ DOM: สำหรับการดำเนินการใดๆ ที่จัดการ DOM โดยตรง (การโฟกัส, การเลื่อน, อนิเมชั่น) ตรวจสอบให้แน่ใจว่าได้ทำภายในuseEffecthook เพื่อรับประกันว่าองค์ประกอบ DOM นั้นมีอยู่จริง- หลีกเลี่ยงการใช้แทน State มากเกินไป: อย่าใช้
useRefเพื่อเก็บข้อมูลที่ *ควร* จะกระตุ้นให้เกิดการอัปเดต UI ให้ใช้useStateสำหรับกรณีดังกล่าวเพื่อรักษาพฤติกรรมของคอมโพเนนต์ที่คาดเดาได้ - จัดทำเอกสารสำหรับโค้ดแบบ Imperative: หากคุณใช้ ref สำหรับการดำเนินการแบบ imperative ให้เพิ่มความคิดเห็นเพื่ออธิบายว่าเหตุใดการจัดการ DOM โดยตรงจึงจำเป็น สิ่งนี้สำคัญอย่างยิ่งสำหรับการรีวิวโค้ดที่เกี่ยวข้องกับนักพัฒนาจากภูมิหลังที่แตกต่างกัน
- พิจารณาใช้ Context สำหรับ Mutable State ที่ใช้ร่วมกัน: สำหรับ state ที่เปลี่ยนแปลงได้ซึ่งจำเป็นต้องใช้ร่วมกันในหลายคอมโพเนนต์และไม่ได้ผูกติดกับองค์ประกอบ DOM ที่เฉพาะเจาะจง ให้พิจารณาใช้ Context API ร่วมกับ
useRefหรือไลบรารีการจัดการ state - ทดสอบบนอุปกรณ์และเครือข่ายที่หลากหลาย: เมื่อใช้ ref สำหรับการดำเนินการที่สำคัญต่อประสิทธิภาพ ให้ทดสอบแอปพลิเคชันของคุณบนอุปกรณ์และสภาพเครือข่ายที่หลากหลายเพื่อให้แน่ใจว่าการทำงานมีความสอดคล้องกันทั่วโลก
สรุป
useRef hook เป็นเครื่องมือที่ขาดไม่ได้ในคลังเครื่องมือของนักพัฒนา React ความสามารถในการจัดการค่าที่เปลี่ยนแปลงได้โดยไม่กระตุ้นให้เกิดการ re-render และการให้สิทธิ์เข้าถึงองค์ประกอบ DOM โดยตรงทำให้มันมีความสำคัญอย่างยิ่งต่อการสร้างแอปพลิเคชันที่มีประสิทธิภาพ โต้ตอบได้ และบำรุงรักษาง่าย ด้วยการทำความเข้าใจหลักการสำคัญและนำแนวทางปฏิบัติที่ดีที่สุดไปใช้ โดยเฉพาะอย่างยิ่งในบริบทการพัฒนาระดับโลก คุณสามารถใช้ประโยชน์จาก useRef เพื่อสร้างประสบการณ์ผู้ใช้ที่มีประสิทธิภาพ เข้าถึงได้ และแข็งแกร่งยิ่งขึ้นสำหรับผู้ใช้ทั่วโลก
ไม่ว่าคุณจะกำลังปรับปรุงประสิทธิภาพของตัวจับเวลา จัดการโฟกัส หรือติดตามสถานะก่อนหน้า useRef ช่วยให้คุณสามารถเขียนโค้ด React ที่สะอาดและมีประสิทธิภาพมากขึ้น จงยอมรับความสามารถของมันและยกระดับแนวทางการพัฒนา frontend ของคุณเพื่อตอบสนองความต้องการของภูมิทัศน์ดิจิทัลระดับโลก